package com.zm.campus.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zm.campus.dao.SchoolMapper;
import com.zm.campus.dao.UserMapper;
import com.zm.campus.pojo.RespBean;
import com.zm.campus.pojo.RespBeanEnum;
import com.zm.campus.pojo.School;
import com.zm.campus.pojo.User;
import com.zm.campus.service.LoginService;
import com.zm.campus.util.*;
import com.zm.campus.vo.LoginReturnVo;
import com.zm.campus.vo.LoginVo;
import com.zm.campus.vo.RegisterVo;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.OnClose;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.zm.campus.service.impl.WebSocketServer.getOnlineCount;

@Service
public class LoginServiceImpl extends ServiceImpl<UserMapper, User> implements LoginService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SchoolMapper schoolMapper;
    @Autowired
    private WebSocketServer webSocketServer;


    //用户登录
    @Override
    public RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {
        //用户名、密码非空校验
        AssertUtil.isTrue(StringUtils.isBlank(loginVo.getPassword())||StringUtils.isBlank(loginVo.getId()),
                RespBeanEnum.LoginVoNotNull);
        //查看数据库是否有此用户
        User user = userMapper.selectById(loginVo.getId());
        AssertUtil.isTrue(user==null,RespBeanEnum.UserIdError);//学号不正确或未注册！
        if (redisTemplate.hasKey("limit3Min:"+user.getId())&&(int)redisTemplate.opsForValue().get("limit3Min:"+user.getId())<=0){
                return RespBean.error(RespBeanEnum.Lock15Min,redisTemplate.opsForValue().getOperations().getExpire("limit3Min:"+user.getId()));
        }
        if (!user.getPassword().equals(MD5Util.fromPassTODBPass(loginVo.getPassword(),user.getSalt()))){
            if ( !redisTemplate.hasKey("limit3Min:"+user.getId())){
                redisTemplate.opsForValue().set("limit3Min:"+user.getId(),3,15,TimeUnit.MINUTES);
            }else {
                ValueOperations voper = redisTemplate.opsForValue();
                if ((int)voper.get("limit3Min:"+user.getId())>0){//说明没有输错的机会了，账号锁15分钟
                    voper.set("limit3Min:"+user.getId(),(int)voper.get("limit3Min:"+user.getId())-1,15,TimeUnit.MINUTES);//0改值不更新过期时间
                    if ((int)voper.get("limit3Min:"+user.getId())<=0){
                        return RespBean.error(RespBeanEnum.Lock15Min,voper.getOperations().getExpire("limit3Min:"+user.getId()));
                    }
                }else{
                    return RespBean.error(RespBeanEnum.Lock15Min,voper.getOperations().getExpire("limit3Min:"+user.getId()));
                }
            }
            return RespBean.error(RespBeanEnum.PassWordError,redisTemplate.opsForValue().get("limit3Min:"+user.getId()));//密码有误
        }
        if (WebSocketServer.isOnline(loginVo.getId())){
            return RespBean.error(RespBeanEnum.isOnline);
        }
        //通过雪花算法工具类生成cookie
        String token=String.valueOf(new SnowflakeIdGenerator(1,1).nextId());

        LoginReturnVo loginReturnVo=new LoginReturnVo();
        loginReturnVo.setUserId(user.getId());
        loginReturnVo.setToken(token);
        loginReturnVo.setRole(user.getRole());//当前登录用户的角色
        loginReturnVo.setAvatar(user.getAvatar());
        // 使用分布式session，把session存到redis
        //不管有没有缓存登录的用户信息，都进行更新
        redisTemplate.opsForValue().set("user:"+token,loginReturnVo,2,TimeUnit.HOURS);//专门操作string的数据类型

        return RespBean.success(loginReturnVo);
    }


    @Override
    public RespBean updatePasssword(String userTicket, HttpServletRequest request, HttpServletResponse response) {
        return null;
    }

    //注册用户
    @Override
    public RespBean register(RegisterVo registerVo) {
        //注册参数合法性校验
        String id = registerVo.getId().trim();//去除id前后空格
        String trueName = registerVo.getTrueName().trim();//去除姓名前后空格
        String password = registerVo.getPassword();
        String phone = registerVo.getPhone();
        String againPassword = registerVo.getAgainPassword();
        Integer schoolId = registerVo.getSchoolId();
        AssertUtil.isTrue(schoolMapper.selectById(schoolId)==null,RespBeanEnum.SchoolIsNull);
        AssertUtil.isTrue(StringUtils.isBlank(id)|| StringUtils.isBlank(trueName)||StringUtils.isBlank(password)
                ||StringUtils.isBlank(phone) ,RespBeanEnum.RegisterNotNull);//注册信息不能为空
        AssertUtil.isTrue(!password.equals(againPassword),RespBeanEnum.TwoPassWordNotSame);//新旧密码要相同
        AssertUtil.isTrue(schoolMapper.selectById(schoolId)==null,RespBeanEnum.SchoolIsNull);
        AssertUtil.isTrue(!PhoneUtil.isMobile(phone),RespBeanEnum.PhoneIncorrect);//手机号码格式不正确
        AssertUtil.isTrue(id.length()>12||id.length()<5,RespBeanEnum.RegisterIdNotTrue);//学号长度不符
        //先查询数据库，看看注册的用户是否已经存在了
        User temp = userMapper.selectById(id);
        AssertUtil.isTrue(temp!=null,RespBeanEnum.UserAlreadyExists);
        //设置默认值，执行插入操作判断受影响行数
        User user= new User();
        user.setId(id);
        user.setSchoolId(schoolId);
        //获取随机盐
        String salt= MD5Util.getSalt(6);
        user.setPassword(MD5Util.inputPassToDBPass(password,salt));
        user.setUserName(trueName);
        user.setPhone(phone);
        user.setSalt(salt);
        user.setEnabled(1);
        user.setCreateDate(new Date());
        user.setAvatar("默认头像.png");//因为要存images\\XXX.png,两个\\会转译成一个\，所以这里需要4个\
        user.setRole("ordinary");//当前用户的角色(ordinary普通用户，admin管理员,superadmin超级管理员)
        AssertUtil.isTrue(userMapper.insert(user)!=1,RespBeanEnum.RegisterFail);//注册异常
        return RespBean.success();
    }

    //退出登录
    @Override
    public RespBean loginOut(String userId,HttpServletResponse response,HttpServletRequest request) {
        webSocketServer.onCloseById(userId);
        System.out.println("当前在线人数为："+getOnlineCount());
        return RespBean.success();
    }

    //获取所有学校列表，有参数是获取
    @Override
    public RespBean getSchoolList(String userId) {
        if (StringUtils.isEmpty(userId)){//用户id是空说明获取的是学校
            List<School> schoolList =(List<School>) redisTemplate.opsForValue().get("schoolList");
            if (schoolList!=null){//说明redis中有学校列表,把数据返回
                return RespBean.success(schoolList);
            }else{
                List<School> school=  schoolMapper.queryAllSchool();
                AssertUtil.isTrue(school==null,RespBeanEnum.SystemBusy);
                redisTemplate.opsForValue().set("schoolList",school);//如果有新的学校加盟，再更新缓存里面的学校列表数据
                return RespBean.success(school);
            }
        }else {//userId不为空说明要查的是学院列表
            User user = userMapper.selectById(userId);
            AssertUtil.isTrue(user==null,RespBeanEnum.ReleaseUserNull);//用户不存在
            List<String> deptList;
            String redisDepartments = (String) redisTemplate.opsForValue().get("getDepartmensBySchoolId:" + user.getSchoolId());
            if (StringUtils.isNotBlank(redisDepartments)){//redis中有对应学校的学院列表
                //处理成ArraysList再传
                String substring = redisDepartments.substring(1, redisDepartments.length() - 1);
                String[] split = substring.split(",");//将字符串用逗号切割开来得到一个字符串数组,比如 String[] substr={"xx","aaa","sss"}
                deptList=Arrays.asList(split);//把字符串数组转为ArraysList,再返回给前端
                return RespBean.success(deptList);
            }else{//redis没有当前学校的学院列表，查询再放到缓存服务器，再返回
               String dbDepartments= schoolMapper.selectDepartmentsBySchoolId(user.getSchoolId());
               AssertUtil.isTrue(StringUtils.isBlank(dbDepartments),RespBeanEnum.SystemBusy);
                redisTemplate.opsForValue().set("getDepartmensBySchoolId:"+user.getSchoolId(),dbDepartments);//存到redis是字符串格式
                //同样是字符串转成字符串数组再转成ArraysList再返回
                String[] split = dbDepartments.substring(1, dbDepartments.length() - 1).split(",");
                deptList=Arrays.asList(split);
                return RespBean.success(deptList);
            }
        }

    }

    @Override
    public RespBean getUserSalt(String userId) throws UnsupportedEncodingException {
        AssertUtil.isTrue(StringUtils.isBlank(userId),RespBeanEnum.BIND_ERROR);
        User user = userMapper.selectById(userId);
        AssertUtil.isTrue(null==user,RespBeanEnum.BIND_ERROR);
        final Base64.Encoder encoder = Base64.getEncoder();
        String salt = encoder.encodeToString(user.getSalt().getBytes("UTF-8"));//将用户的盐用base64加密穿给前端进行前端的第一次MD5加密
        return RespBean.success(salt);
    }

    @Override
    public RespBean getLoginAvatar(String userId) {
        User user = userMapper.selectById(userId);
        AssertUtil.isTrue(null==user||StringUtils.isBlank(userId),RespBeanEnum.UserIdError);
        return RespBean.success(user.getAvatar());
    }
}
